<template>
  <div>
    <div><h3>文件上传</h3></div>
    <el-upload
      ref="sliceUpload"
      action="#"
      :limit=1
      :file-list="[]"
      :auto-upload="false"
      :http-request="handSliceUpload">
      <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
      <el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button>
      <el-button size="small" v-if="isPaused" type="info" @click="handlePause">暂停</el-button>
      <el-button size="small" v-else @click="handleResume">恢复</el-button>
      <div slot="tip" class="el-upload__tip">大文件分片上传</div>
    </el-upload>
    <div>
      <el-progress title="hash进度" :percentage="hashPercentage"/>
      <!-- 总进度 -->
      <el-progress title="总进度" :percentage="fakeUploadPercentage"/>
      <div v-show="downloadInfo.fileUrl" style="text-align: start">
        <div>文件名：{{ downloadInfo.fileName }}</div>
        <div>下载地址：{{ downloadInfo.fileUrl }}</div>
      </div>
      <div><h3>上传分片文件列表</h3></div>
      <el-table :data="data" style="width: 800px">
        <el-table-column prop="hash" label="切片hash">
          <template v-slot="scope">{{ scope.row.hash }}-{{ scope.row.index }}</template>
        </el-table-column>
        <el-table-column prop="chunk" label="大小(KB)" width="100">
          <template v-slot="scope">{{ scope.row.chunk | getSizeKB }}</template>
        </el-table-column>
        <el-table-column prop="percentage" label="进度" width="400px">
          <template v-slot="scope">
            <el-progress :percentage="scope.row.percentage"/>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>

<script>
import FileSlicer from '@/utils/fileSlicer'

export default {
  name: 'SliceUpload',
  data () {
    return {
      // 是否暂停
      isPaused: true,
      // hash进度
      hashPercentage: 0,
      // 总进度
      fakeUploadPercentage: 0,
      // 详细数据
      data: [],
      // uploadSuccessData: [],
      // 文件file信息
      fileSlicer: null,
      // 分片大小（以2MB为一个分片）
      chunkSize: 1.5 * 1024 * 1024,
      // 下载信息
      downloadInfo: {
        fileName: undefined,
        // 文件回显地址
        fileUrl: undefined
      }
    }
  },
  filters: {
    getSizeKB (chunk) {
      return (chunk.size / 1024).toFixed(0) || 0
    }
  },
  methods: {
    submitUpload () {
      this.$refs.sliceUpload.submit()
    },
    async handSliceUpload (request) {
      console.log('开始处理。。。', request)
      // 检查文件后缀
      const fileSuffix = this.getFileSuffix(request.file.name)
      console.log(fileSuffix)
      this.fileSlicer = new FileSlicer(request.file).slice(this.chunkSize)
      // 计算文件hash值
      await new Promise((resolve, reject) => {
        this.fileSlicer.hash(({
          percentage,
          hash
        }) => {
          this.hashPercentage = percentage
          if (hash) {
            resolve(hash)
          }
        })
      })
      console.log(this.fileSlicer)
      // 文件秒传：验证文件是否存在
      const {
        shouldUpload,
        uploadedList,
        fileName,
        fileUrl
      } = await this.verifyFile()
      console.log(shouldUpload, uploadedList)
      if (!shouldUpload) {
        this.$message.success('秒传：上传成功')
        this.downloadInfo.fileUrl = fileUrl
        this.downloadInfo.fileName = fileName
        return
      }
      this.data = this.fileSlicer.chunkList.map((chunk, index) => ({
        chunk: chunk,
        index,
        hash: this.fileSlicer.fileHash,
        percentage: uploadedList?.includes(index) ? 100 : 0
      }))
      console.log(this.data, '00000000000000000000000000000000000000000000000000000')
      await this.uploadChunks(uploadedList)
    },
    // 暂停
    handlePause () {
      this.isPaused = false
    },
    // 恢复
    async handleResume () {
      this.isPaused = true
      const { uploadedList } = await this.verifyFile()
      await this.uploadChunks(uploadedList)
    },
    // 文件验证
    async verifyFile () {
      const { data } = await this.$http.post('/file/verify', {
        fileName: this.fileSlicer.file.name,
        fileHash: this.fileSlicer.fileHash
      })
      return data.data
    },
    // 分片上传
    async uploadChunks (uploadedList = []) {
      const requestList = this.data
        // 过滤已上传的hash切片
        .filter(({
          hash,
          index
        }) => !uploadedList.includes(`${hash}-${index}`))
        .map(async ({
          chunk,
          hash,
          index
        }) => {
          const formData = new FormData()
          formData.append('chunk', chunk)
          formData.append('hash', hash)
          formData.append('chunkIndex', index)
          return this.$http.post('/file/upload', formData, {
            onUploadProgress: progressEvent => {
              this.data[index].percentage = parseInt(
                String((progressEvent.loaded / progressEvent.total) * 100))
              // if (this.data[index].percentage === 100) {
              //   this.uploadSuccessData.push(this.data[index])
              // }
            }
          })
        })
      await Promise.all(requestList)
      // 合并切片
      if (uploadedList.length + requestList.length === this.data.length) {
        await this.mergeRequest()
      }
    },
    mergeRequest () {
      this.$http.post('/file/merge', {
        fileName: this.fileSlicer.file.name,
        fileHash: this.fileSlicer.fileHash,
        fileSuffix: this.getFileSuffix(this.fileSlicer.file.name),
        chunkTotal: this.fileSlicer.chunkList.length
      }).then(response => {
        const {
          code,
          message,
          data
        } = response.data
        if (code === 200) {
          this.$message({
            message: message || '上传成功',
            type: 'success'
          })
          this.downloadInfo.fileUrl = data.fileUrl
          this.downloadInfo.fileName = data.fileName
          console.log('this.container', this.fileSlicer, data)
        }
      })
    },
    // 获取文件后缀
    getFileSuffix (fileName) {
      const fileSuffix = fileName.split('.').pop()
      if (!fileSuffix) {
        throw new Error('文件格式不正确')
      }
      return fileSuffix.toLowerCase()
    }
  }
}
</script>

<style scoped>

</style>
